package com.mycompany.crm.service.impl;

import com.mycompany.crm.base.BaseQuery;
import com.mycompany.crm.base.BaseService;
import com.mycompany.crm.controller.CaptchaController;
import com.mycompany.crm.dao.UserMapper;
import com.mycompany.crm.dao.UserRoleMapper;
import com.mycompany.crm.model.UserModel;
import com.mycompany.crm.service.UserService;
import com.mycompany.crm.utils.*;
import com.mycompany.crm.vo.User;
import com.mycompany.crm.vo.UserRole;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 用户登录业务逻辑层
 * 用户登录
 1. 参数判断，判断用户姓名、用户密码非空
 如果参数为空，抛出异常（异常被控制层捕获并处理）
 2. 调用数据访问层，通过用户名查询用户记录，返回用户对象
 3. 判断用户对象是否为空
 如果对象为空，抛出异常（异常被控制层捕获并处理）
 4. 判断密码是否正确，比较客户端传递的用户密码与数据库中查询的用户对象中的用户密码
 如果密码不相等，抛出异常（异常被控制层捕获并处理）
 5. 如果密码正确，登录成功
 * @author jy
 * @version 1.0
 * @date 2022/1/4 16:14
 */
@Service
public class UserServiceImpl extends BaseService<User,Integer> implements UserService {
    @Resource
    private UserMapper userMapper;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 用户登录
     * @param userName 用户名
     * @param userPwd 用户密码
     * @return 返回给页面的用户数据
     */
    @Override
    public UserModel userLogin(HttpServletRequest request,String userName, String userPwd, String verifyCode){
        //1. 参数判断，判断用户姓名、用户密码非空
        checkLoginParams(userName,userPwd);
        //2. 调用数据访问层，通过用户名查询用户记录，返回用户对象
        User user = userMapper.queryUserByName(userName);
        String key = CaptchaController.getCaptchaKey(request);
        String redisCode = redisTemplate.opsForValue().get(key);
        AssertUtil.isTrue(redisCode == null,"验证码已过期！请点击图片重新获取！");
        AssertUtil.isTrue(!verifyCode.equals(redisCode),"验证码不正确！");
        //3. 判断用户对象是否为空
        AssertUtil.isTrue(user == null,"用户名不存在！");
        //4. 判断密码是否正确，比较客户端传递的用户密码与数据库中查询的用户对象中的用户密码
        assert user != null;
        checkUserPwd(userPwd,user.getUserPwd());
        //5.返回构建用户对象
        return buildUserInfo(user);
    }

    /**
     * 构建需要返回给客户端的用户对象
     * @param user 用户对象
     */
    private UserModel buildUserInfo(User user) {
        UserModel userModel = new UserModel();
//        userModel.setUserId(user.getId());
        userModel.setUserIdStr(UserIDBase64.encoderUserID(user.getId()));
        userModel.setUserName(user.getUserName());
        userModel.setTrueName(user.getTrueName());
        return userModel;
    }

    /**
     * 用户基本资料修改
     *
     * @param userId
     * @param userName
     * @param trueName
     * @param email
     * @param phone
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void updateUserInfo(Integer userId, String userName, String trueName, String email, String phone,String userImg) {
        //通过用户ID查询用户记录，返回用户对象
        User user = userMapper.selectByPrimaryKey(userId);
        //1.参数校验
        checkUserInfoParams(user,userName,trueName,email,phone,userImg);
        //2.设置用户新资料
        user.setUserName(userName);
        user.setTrueName(trueName);
        user.setEmail(email);
        user.setPhone(phone);
        user.setUserImg(userImg);
        //3.执行更新操作
        AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user) < 1,"用户基本资料失败！");
    }

    /**
     * 验证用户资料修改参数
     * @param user
     * @param userName
     * @param trueName
     * @param email
     * @param phone
     */
    private void checkUserInfoParams(User user, String userName, String trueName, String email, String phone,String userImg) {
        //user对象，非空验证
        AssertUtil.isTrue(user == null,"用户未登录或不存在!");
        //邮箱格式校验
        AssertUtil.isTrue(!EmailUtil.checkEmail(email),"邮箱格式不正确！");
        //手机格式校验
        AssertUtil.isTrue(!PhoneUtil.isMobile(phone),"手机号码格式不正确！");
    }

    /**
     * 用户密码修改
     *     1. 参数校验
     *         用户ID：userId 非空 用户对象必须存在
     *         原始密码：oldPassword 非空 与数据库中密文密码保持一致
     *         新密码：newPassword 非空 与原始密码不能相同
     *         确认密码：confirmPassword 非空 与新密码保持一致
     *     2. 设置用户新密码
     *         新密码进行加密处理
     *     3. 执行更新操作
     *         受影响的行数小于1，则表示修改失败
     *
     *     注：在对应的更新方法上，添加事务控制
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void updatePassword(Integer userId,String oldPwd,String newPwd,String repeatPwd){
        //通过用户ID查询用户记录，返回用户对象
        User user = userMapper.selectByPrimaryKey(userId);
        //1.参数校验
        checkPasswordParams(user,oldPwd,newPwd,repeatPwd);
        //2.设置用户新密码
        user.setUserPwd(Md5Util.encode(newPwd));
        //3.执行更新操作
        AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user) < 1,"密码更新失败！");
    }

    /**
     * 验证用户密码修改参数
     *      用户ID：userId 非空 用户对象必须存在
     *      原始密码：oldPassword 非空 与数据库中密文密码保持一致
     *      新密码：newPassword 非空 与原始密码不能相同
     *      确认密码：confirmPassword 非空 与新密码保持一致
     * @param user 当前登录的用户名
     * @param oldPwd 旧密码
     * @param newPwd 新密码
     * @param repeatPwd 再次输入新密码
     */
    private void checkPasswordParams(User user, String oldPwd, String newPwd, String repeatPwd) {
        //user对象，非空验证
        AssertUtil.isTrue(user == null,"用户未登录或不存在!");
        //原始密码，非空验证
        AssertUtil.isTrue(StringUtils.isBlank(oldPwd),"请输入原始密码!");
        //原始密码必须与数据库中加密后的密码一致
        assert user != null;
        AssertUtil.isTrue(!user.getUserPwd().equals(Md5Util.encode(oldPwd)),"原始密码输入错误！");
        //新密码,非空验证
        AssertUtil.isTrue(StringUtils.isBlank(newPwd),"请输入新密码！");
        //新密码与原始密码不能相同
        AssertUtil.isTrue(oldPwd.equals(newPwd),"新密码不能与原始密码相同！");
        //确认密码，非空验证
        AssertUtil.isTrue(StringUtils.isBlank(repeatPwd),"请输入确认密码！");
        //新密码要与确认密码一致
        AssertUtil.isTrue(!newPwd.equals(repeatPwd),"新密码与确认密码不一致！");
    }


    /**
     * 密码判断
     *    先将客户端传递的密码加密，再与数据库中查询到的密码作比较
     * @param userPwd 用户输入的密码
     * @param pwd 数据库中的密码
     */
    private void checkUserPwd(String userPwd, String pwd) {
        // 将客户端传递的密码加密,再与数据库中的密码进行比较
        userPwd = Md5Util.encode(userPwd);
        // 比较密码
        assert userPwd != null;
        AssertUtil.isTrue(!userPwd.equals(pwd),"输入的密码不正确！");
    }

    /**
     * 查询所有的销售人员
     * @return List<Map<String,Object>> 所有销售人员对象的map集合的列表
     */
    @Override
    public List<Map<String,Object>> queryAllSales(){
        return userMapper.queryAllSales();
    }

    /**
     * 分页多条件查询用户列表
     * @param baseQuery 基本的分页查询管理类
     * @return 用户列表
     */
    @Override
    public Map<String, Object> queryByParamsForTable(BaseQuery baseQuery) {
        return super.queryByParamsForTable(baseQuery);
    }

    /**
     * 添加用户
     *   1.参数校验
     *       用户名userName：非空，唯一性
     *       邮箱email：  非空
     *       手机号phone  非空，格式合法
     *   2.设置默认参数
     *       isValid  1
     *       createDate  当前时间
     *       updateDate  更新时间
     *       userpwd  123456 -> md5加密
     *   3，执行添加，判断结果
     *   @param user 传入的用户对象
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void saveUser(User user){
        //1.参数校验
        checkUserParams(user.getUserName(),user.getEmail(),user.getPhone(),null);
        //2.设置默认参数
        user.setIsValid(1);
        user.setCreateDate(new Date());
        user.setUpdateDate(new Date());
        user.setUserPwd(Md5Util.encode("123456"));
        //执行添加，判断结果
        AssertUtil.isTrue(userMapper.insertSelective(user) < 1,"用户添加失败！");
        //用户角色关联
        relationUserRole(user.getId(),user.getRoleIds());
    }

    /**
     * 更新用户
     *   1.参数校验
     *       判断用户id是否为空，且数据存在
     *       用户名userName：非空，唯一性
     *       邮箱email：  非空
     *       手机号phone  非空，格式合法
     *   2.设置默认参数
     *       updateDate  系统当前时间
     *   3，执行更新，判断受影响的行数
     * @param user 传入的用户对象
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void updateUser(User user){
        User temp = userMapper.selectByPrimaryKey(user.getId());
        //判断用户id是否为空且数据存在
        AssertUtil.isTrue(null == user.getId() || null == temp,"待更新记录不存在！");
        //参数校验
        checkUserParams(user.getUserName(),user.getEmail(),user.getPhone(),user.getId());
        //设置默认值
        user.setUpdateDate(new Date());
        //执行更新操作，判断受影响的行数
        AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user) != 1,"用户更新失败！");
        //用户角色关联
        relationUserRole(user.getId(),user.getRoleIds());
    }

    /**
     * 用户角色关联
     * @param userId 用户id
     * @param roleIds 用户对应的角色ids(通过前端下拉框的选择传给后台的ids)
     *
     * 如何进行角色分配?
     *    先判断用户对应的角色记录是否存在，然后将用户所有的角色全部清除，再把页面最终赋予的角色添加上去
     */
    private void relationUserRole(Integer userId,String roleIds){
        //查询用户id对应的角色记录
        Integer count = userRoleMapper.selectCountUserRoleByUserId(userId);
        //判断角色记录是否存在
        if (count > 0){
            //如果存在则先统一把角色记录全部删除
            AssertUtil.isTrue(!userRoleMapper.deleteUserRoleByUserId(userId).equals(count),"用户角色分配失败！");
        }
        //判断角色Id是否存在，如果存在，则添加该用户对应的角色记录
        if (StringUtils.isNotBlank(roleIds)){
            //将用户的角色数据设置到集合中，执行批量添加
            List<UserRole> list = new ArrayList<>();
            //将角色id字符串转为数组
            String[] roleIdsArray = roleIds.split(",");
            for (String roleIdStr : roleIdsArray) {
                UserRole userRole = new UserRole();
                userRole.setRoleId(Integer.parseInt(roleIdStr));
                userRole.setUserId(userId);
                userRole.setCreateDate(new Date());
                userRole.setUpdateDate(new Date());
                //设置到集合中
                list.add(userRole);
            }
            //批量添加用户角色记录
            AssertUtil.isTrue(userRoleMapper.insertBatch(list) != list.size(),"用户角色分配失败！");
        }
    }


    /**
     * 删除用户
     * @param ids 页面传入的id，可能一个可能多个
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void deleteUserByIds(Integer[] ids){
        AssertUtil.isTrue(ids == null || ids.length < 1,"待删除的记录不存在！");
        assert ids != null;
        AssertUtil.isTrue(userMapper.deleteBatch(ids) != ids.length,"用户记录删除失败！");
    }


    /**
     * 查询所有的客户经理
     * @return
     */
    @Override
    public List<Map<String, Object>> queryAllCustomerManagers() {
        return userMapper.queryAllCustomerManagers();
    }


    /**
     * 登录参数校验
     *    如果参数为空， 抛出异常(异常被控制层捕获并处理)
     * @param userName 登录传入的用户名
     * @param userPwd 登录传入的密码
     */
    private void checkLoginParams(String userName, String userPwd) {
        //验证用户名(isBlank()方法的参数如果为空则返回true,isTrue()第一个参数为true则抛出第二个参数的异常信息)
        AssertUtil.isTrue(StringUtils.isBlank(userName),"用户名不能为空！");
        //验证用户密码(原理同上)
        AssertUtil.isTrue(StringUtils.isBlank(userPwd),"用户密码不能为空！");
    }



    /**
     * 用户数据完整性参数校验
     * @param userName 用户名
     * @param email 邮箱
     * @param phone 电话号码
     */
    private void checkUserParams(String userName, String email, String phone,Integer userId) {
        // 判断用户名是否为空
        AssertUtil.isTrue(StringUtils.isBlank(userName),"用户名不能为空！");
        // 判断用户名的唯一性
        // 通过用户名查询用户对象
        User user = userMapper.queryUserByName(userName);
        // 如果用户对象为空，则表示用户名可用；如果用户对象不为空，则表示用户名不可用
        // 如果是添加操作，数据库中无数据，只要通过名称查到数据，则表示用户名被占用
        // 如果是修改操作，数据库中有对应的记录，通过用户名查到数据，可能是当前记录本身，也可能是别的记录
        // 如果用户名存在，且与当前修改记录不是同一个，则表示其他记录占用了该用户名，不可用
        AssertUtil.isTrue(null != user && !(user.getId().equals(userId)),"用户名已存在！");
        // 邮箱 非空
        AssertUtil.isTrue(StringUtils.isBlank(email),"请输入邮箱地址！");
        // 手机号 非空
        AssertUtil.isTrue(StringUtils.isBlank(phone),"请输入手机号码！");
        // 手机号 格式判断
        AssertUtil.isTrue(!PhoneUtil.isMobile(phone),"请输入正确的手机号码！");
    }

}
